package client;

import infrastructure.CommunicationCustomException;
import infrastructure.KVMessageImp;

import java.util.HashSet;
import java.util.Queue;
import java.util.Set;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.SynchronousQueue;

import common.messages.KVMessage;
import common.messages.KVMessage.StatusType;

import clientCommunication.ClientSocketListener;
import clientCommunication.Communication;
import clientCommunication.ICommunication;

public class KVStore implements KVStorageInterface, ClientSocketListener {

	private String ipaddress;
	int port;
	private ICommunication communication;
	private Queue<KVMessage> messageQueue;
	
	private Set<KVStorageListener> listeners;
	
	/**
	 * Initialize KVStore with address and port of KVServer
	 * @param address the address of the KVServer
	 * @param port the port of the KVServer
	 */
	public KVStore(String ip, int port) {
		listeners = new HashSet<KVStorageListener>();
		this.ipaddress = ip;
		this.port = port;		
		messageQueue = new LinkedBlockingQueue<KVMessage>(10);
	}
	
	@Override
	public void connect() throws Exception {		
		this.communication = new Communication(this.port + "", this.ipaddress);
		this.communication.addListener(this);
		Thread t = new Thread(communication);
		t.start();
	}

	@Override
	public void disconnect() {
		if (communication != null){
			communication.disconnect();
		}
		
		communication = null;
	}

	@Override
	public synchronized KVMessage put(String key, String value) throws Exception {
		if (communication == null)
		{
			throw new Exception("Communication is not stablished");
		}
		
		communication.SendMessage("put " + key + " " + value);
		wait();
		KVMessage message = messageQueue.remove();
		return message;
	}

	@Override
	public synchronized KVMessage get(String key) throws Exception {
		if (communication == null)
		{
			throw new Exception("Communication is not stablished");
		}
		
		communication.SendMessage("get " + key);
		wait();
		KVMessage message = messageQueue.remove();
		return message;
	}

	@Override
	public void AddListener(KVStorageListener listener) {
		this.listeners.add(listener);
	}

	@Override
	public synchronized void handleNewMessage(Object msg) {		
		KVMessage kvMessage = null;
		if (KVMessage.class.isAssignableFrom(msg.getClass())) { 
			kvMessage = (KVMessage)msg;
		} 
		
		if (kvMessage == null) {
			String textMessage = (String)msg;
			for(KVStorageListener listener : this.listeners) {
				listener.handleReply("Reply from server: " + textMessage);
			}
		} else {
			messageQueue.add(kvMessage);			
		}
		
		notify();
	}

	@Override
	public void handleStatus(SocketStatus status) {
		// Just notify the client in  a simple way
		for(KVStorageListener listener : this.listeners) {
			listener.handleReply("Server is: " + status.toString());
		}
	}
}
